package org.aceor.mddal.gateway.sqlparser;

import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.ast.statement.SQLDeleteStatement;
import com.alibaba.druid.sql.ast.statement.SQLInsertStatement;
import com.alibaba.druid.sql.ast.statement.SQLSelectStatement;
import com.alibaba.druid.sql.ast.statement.SQLUpdateStatement;
import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser;
import com.alibaba.druid.sql.visitor.SQLASTOutputVisitor;
import org.aceor.mddal.common.monitor.Monitor;
import org.aceor.mddal.common.monitor.MonitorPoint;
import org.aceor.mddal.common.sqlparser.MysqlVisitor;
import org.aceor.mddal.common.sqlparser.result.SQLParsedResult;
import org.aceor.mddal.common.sqlparser.result.context.ShardingContext;
import org.aceor.mddal.common.sqlparser.result.object.SQLStatementType;
import org.aceor.mddal.gateway.sqlparser.visitor.MysqlDeleteVisitor;
import org.aceor.mddal.gateway.sqlparser.visitor.MysqlInsertVisitor;
import org.aceor.mddal.gateway.sqlparser.visitor.MysqlSelectVisitor;
import org.aceor.mddal.gateway.sqlparser.visitor.MysqlUpdateVisitor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.Map;

/**
 * Created by lxue on 16/4/11.
 */
public class SQLParserEngine {
    private static final Logger log = LoggerFactory.getLogger(SQLParserEngine.class);

    private final String originalSql;

    private final SQLStatement sqlStatement;

    private final List<Object> parameters;

    private final SQLASTOutputVisitor visitor;

    private SQLStatementType sqlStatementType;

    private Map<String, ShardingContext> allTableShardingColumnsMapping;

    public SQLParserEngine(String originalSql, List<Object> parameters, Map<String, ShardingContext> allTableShardingColumnsMapping) {
        this.originalSql = originalSql;
        this.sqlStatement = new MySqlStatementParser(originalSql).parseStatement();
        this.parameters = parameters;
        this.visitor = getSQLASTOutputVisitor(sqlStatement);
        this.allTableShardingColumnsMapping = allTableShardingColumnsMapping;
    }

    private SQLASTOutputVisitor getSQLASTOutputVisitor(SQLStatement sqlStatement) {
        if (sqlStatement instanceof SQLSelectStatement) {
            sqlStatementType = SQLStatementType.SELECT;
            return new MysqlSelectVisitor();
        } else if (sqlStatement instanceof SQLUpdateStatement) {
            sqlStatementType = SQLStatementType.UPDATE;
            return new MysqlUpdateVisitor();
        } else if (sqlStatement instanceof SQLDeleteStatement) {
            sqlStatementType = SQLStatementType.DELETE;
            return new MysqlDeleteVisitor();
        } else if (sqlStatement instanceof SQLInsertStatement) {
            sqlStatementType = SQLStatementType.INSERT;
            return new MysqlInsertVisitor();
        } else {
            throw new IllegalArgumentException("未找到对应的SQLASTOutputVisitor类型, sqlStatement :"+sqlStatement);
        }
    }

    /**
     *  解析SQL.
     *
     * @return SQL解析结果
     */
    public SQLParsedResult parse() {
        try {
            //启动sql解析监控
            Monitor.startSqlParseMonitor(originalSql);
            //设置sql参数,用于支持?占位符
            visitor.setParameters(parameters);
            //将Druid的vistor对象强转成我们自定义实现的接口类型
            MysqlVisitor sqlVisitor = (MysqlVisitor) visitor;
            //设置分库分表字段映射
            sqlVisitor.getParseContext().setTableShardingColumnsMapping(allTableShardingColumnsMapping);
            //Visitor模式解析各节点
            sqlStatement.accept(visitor);
            //从解析上下文中拿出解析结果
            SQLParsedResult result = sqlVisitor.getParseContext().getSqlParsedResult();
            //设置sql重构构造器
            result.routeContext.setSqlBuilder(sqlVisitor.getSQLBuilder());
            //设置sql语句类型(INSERT/UPDATE/SELECT/DELETE)
            result.routeContext.setSqlStatementType(sqlStatementType);
            log.info("Sql :{}\nParse Result :{}", originalSql, result);
            return result;
        } finally {
            //完成sql解析监控
            Monitor.stop(MonitorPoint.PARSE_SQL);
        }
    }


    private SQLStatementType getType() {
        if (sqlStatement instanceof SQLSelectStatement) {
            return SQLStatementType.SELECT;
        }
        if (sqlStatement instanceof SQLInsertStatement) {
            return SQLStatementType.INSERT;
        }
        if (sqlStatement instanceof SQLUpdateStatement) {
            return SQLStatementType.UPDATE;
        }
        if (sqlStatement instanceof SQLDeleteStatement) {
            return SQLStatementType.DELETE;
        }
        throw new RuntimeException("Unsupported SQL statement: " + sqlStatement);
    }
}
